Passed
Push — development ( 989440...b94626 )
by Karl
25:06 queued 11:57
created

TravelService.findAll   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
1 9
import {
0 ignored issues
show
introduced by
Replace ...·NotFoundException,⏎ with ·Injectable,·BadReques...n,·NotFoundException·
Loading history...
2
  Injectable,
3
  BadRequestException,
4
  NotFoundException,
5
} from '@nestjs/common';
6 9
import { InjectRepository } from '@nestjs/typeorm';
7 9
import { IsNull, Not, Repository } from 'typeorm';
8 9
import { Travel } from './entities/travel.entity';
9 9
import { BicyclesService } from '../bicycles/bicycles.service';
10 9
import { ZonesService } from 'src/zones/zones.service';
11
12
@Injectable()
13 9
export class TravelService {
14
  async findTravelsForCustomer(customerId: string) {
15
    return await this.travelRepository.find({
16
      where: { customer: { githubId: customerId } },
17
    });
18
  }
19
  constructor(
20
    @InjectRepository(Travel)
21 7
    private readonly travelRepository: Repository<Travel>,
22 7
    private readonly bicyclesService: BicyclesService,
23 7
    private readonly zonesService: ZonesService,
24
  ) { }
0 ignored issues
show
introduced by
Delete ·
Loading history...
25
26
  async findAll(): Promise<Travel[]> {
27 1
    return await this.travelRepository.find();
28
  }
29
30
  async findById(id: number): Promise<Travel> {
31 2
    const travel = await this.travelRepository.findOne({ where: { id } });
32 2
    if (!travel) {
33 1
      throw new NotFoundException('Travel not found');
34
    }
35 1
    return travel;
36
  }
37
38
  async findActiveTravelForBike(bikeId: string): Promise<Travel> {
39
    const activeTravel = await this.travelRepository.findOne({
40
      where: {
41
        bike: { id: bikeId },
42
        startTime: Not(IsNull()),
43
        stopTime: IsNull(),
44
      },
45
    });
46
47 1
    if (!activeTravel) {
48
      throw new NotFoundException(`No active travel found for bike ${bikeId}`);
49
    }
50
51
    return activeTravel;
52
  }
53
54
  async startRentingBike(bikeId: string, customerId: string): Promise<Travel> {
55
0 ignored issues
show
introduced by
Delete
Loading history...
56 1
    const bike = await this.bicyclesService.setRented(bikeId);
57 2
    const zoneType = this.zonesService.pointInParkingZone(bike.latitude, bike.longitude) ? 'Parking' : 'Free';
0 ignored issues
show
introduced by
Replace ·?·'Parking' with ⏎······?·'Parking'⏎·····
Loading history...
58
59 1
    const travel = this.travelRepository.create({
60
      bike,
61
      startTime: new Date(),
62
      latStart: bike.latitude,
63
      longStart: bike.longitude,
64
      customer: { githubId: customerId },
65
      startZoneType: zoneType,
66
      endZoneType: null,
67
      cost: 0,
68
    });
69
70
0 ignored issues
show
introduced by
Delete
Loading history...
71 1
    return this.travelRepository.save(travel);
72
  }
73
74
  async endActiveTravelForBike(bikeId: string) {
75
    const activeTravel = await this.findActiveTravelForBike(bikeId);
76
    return this.endTravel(activeTravel.id);
77
  }
78
79
  async endTravel(travelId: number): Promise<Travel> {
80 2
    const travel = await this.travelRepository.findOne({
81
      where: { id: travelId },
82
      relations: ['bike', 'customer'], // Load bike and customer relations
83
    });
84
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
85 2
    if (!travel) {
86 1
      throw new NotFoundException('Travel not found');
87
    }
88
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
89 1
    if (travel.stopTime) {
90
      throw new BadRequestException('Travel has already ended');
91
    }
92
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
93
    // Get current bike location (from bike entity)
94 1
    const bike = await this.bicyclesService.findById(travel.bike.id);
95
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
96
    // Get the end zone type
97 2
    const endZoneType = this.zonesService.pointInParkingZone(bike.latitude, bike.longitude) ? 'Parking' : 'Free';
0 ignored issues
show
introduced by
Replace ·?·'Parking' with ⏎······?·'Parking'⏎·····
Loading history...
98
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
99
    // Set end time to current server time
100 1
    const endTime = new Date();
101
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
102
    // Calculate cost
103 1
    const cost = this.calculateCost(
0 ignored issues
show
introduced by
Replace ⏎······travel.st...endZoneType,⏎···· with travel.startTime,·endTi...tZoneType,·endZoneType
Loading history...
104
      travel.startTime,
105
      endTime,
106
      travel.startZoneType,
107
      endZoneType,
108
    );
109
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
110
    // Update travel record
111 1
    travel.stopTime = endTime;
112 1
    travel.latStop = bike.latitude;
113 1
    travel.longStop = bike.longitude;
114 1
    travel.endZoneType = endZoneType;
115 1
    travel.cost = cost;
116
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
117
    // Update bike status to available
118 1
    await this.bicyclesService.update(bike.id, { status: 'Available' });
119
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
120
    // Update user account
121 1
    const customer = travel.customer;
122
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
123 2
    if (customer.isMonthlyPayment) {
124
      // Accumulate cost for monthly payment users
125 1
      customer.accumulatedCost += cost;
126
    } else {
127
      // Deduct from balance for prepaid users
128 1
      if (customer.balance < cost) {
129
        throw new BadRequestException('Insufficient balance. Please insert funds.');
130
      }
131
      customer.balance -= cost;
132
    }
133
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
134
    // Save updated user
135 1
    await this.travelRepository.manager.getRepository('User').save(customer);
136
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
137
    // Save and return updated travel
138 1
    return this.travelRepository.save(travel);
139
  }
140
141
  async endAllTravelsForCustomer(githubId: string): Promise<string> {
142
    // Find all active travels for the customer
143
    const activeTravels = await this.travelRepository.find({
144
      where: {
145
        customer: { githubId: githubId },
146
        startTime: Not(IsNull()),
147
        stopTime: IsNull(),
148
      },
149
      relations: ['bike', 'customer'],
150
    });
151
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
152 1
    if (activeTravels.length === 0) {
153
      throw new NotFoundException(
154
        `No active travels found for customer with GitHub ID ${githubId}.`
0 ignored issues
show
introduced by
Insert ,
Loading history...
155
      );
156
    }
157
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
158
    // Loop through each active travel and end it using the existing endTravel method
159
    for (const travel of activeTravels) {
160
      await this.endTravel(travel.id);
161
    }
162
  
0 ignored issues
show
introduced by
Delete ··
Loading history...
163
    return `All active travels for customer with GitHub ID ${githubId} have been successfully ended.`;
164
  }
165
  
0 ignored issues
show
introduced by
Delete ··⏎··⏎
Loading history...
166
  
167
168
  calculateCost(
169
    startTime: Date,
170
    endTime: Date,
171
    startZoneType: string,
172
    endZoneType: string,
173
  ): number {
174 1
    const timeDiff = endTime.getTime() - startTime.getTime();
175 1
    const timeDiffInMinutes = timeDiff / 1000 / 60;
176
177 1
    const parkingFee = 10;
178 1
    const startFee = 10;
179 1
    const costPerMinute = 1;
180
181
    let cost =
0 ignored issues
show
introduced by
'cost' is never reassigned. Use 'const' instead.
Loading history...
182 2
      (endZoneType === 'Parking' ? 0 : parkingFee) +
183
      (startZoneType === 'Free' && endZoneType === 'Parking'
0 ignored issues
show
introduced by
Replace ⏎········?·st...·/·2⏎······· with ·?·startFee·/·2
Loading history...
184
        ? startFee / 2
185
        : startFee) +
186
      timeDiffInMinutes * costPerMinute;
187
188 1
    return cost;
189
  }
190
}
191